// 按键消抖模块
module pb_filter
(
    input                       clk,                //输入时钟信号
    input                       rst_n,              //低电平复位信号
    input                       ms_p,               //输入毫秒脉冲
    input                       key_in,             //输入按键信号
    input                       is_low,             //输入按键是否低电平有效，用于兼容不同器件按键消抖
    output                      key_out             //输出按键信号
);

reg                             keyin_r;            //输入按键临时值
reg                             keyin_rr;           //输入按键操作临时值
reg                             keyout_r;           //输出按键临时值

//push_button vibrating elemination
reg     [1:0]                   ve_key_st;          //按键消抖状态
reg     [3:0]                   ve_key_count;       //按键消抖计数器

//输入按键赋临时值
always @ (posedge clk) begin
    keyin_r <= is_low ? key_in : ~key_in;
    keyin_rr <= keyin_r;
end

//三段式状态机
localparam      T_10ms      = 10;   //消抖计数
localparam      IDLE        = 0;    //空闲状态
localparam      BEFORE_VE   = 1;    //消抖前状态
localparam      STABLE      = 2;    //稳定状态
localparam      AFTER_VE    = 3;    //消抖后状态

//一段计数
always @ (posedge clk) begin
    if (!rst_n) ve_key_count <= 0;
    else if (ms_p && (ve_key_st == BEFORE_VE || ve_key_st == AFTER_VE) && ve_key_count < T_10ms)
        ve_key_count <= ve_key_count + 1;
    else ve_key_count <= 0;
end

//二段状态转移
always @ (posedge clk)
    if (~rst_n) ve_key_st <= IDLE;
    else
    case (ve_key_st)
        IDLE        : if (~keyin_rr) ve_key_st <= BEFORE_VE;
        BEFORE_VE   : begin
            if (keyin_rr) ve_key_st <= IDLE;
            else if (ve_key_count == T_10ms) ve_key_st  <= STABLE;
        end
        STABLE      : if (keyin_rr) ve_key_st <= AFTER_VE;
        AFTER_VE    : begin
            if (~keyin_rr) ve_key_st <= STABLE;
            else if (ve_key_count == T_10ms)  ve_key_st <= IDLE;
        end
        default     : ve_key_st <= IDLE;
    endcase

//三段判断结果
always @ (posedge clk)
    if (~rst_n) keyout_r <= 1'b1;
    else
    case (ve_key_st)
        IDLE        : keyout_r <= 1'b1;
        BEFORE_VE   : keyout_r <= 1'b1;
        STABLE      : keyout_r <= 1'b0;
        AFTER_VE    : keyout_r <= 1'b1;
        default     : keyout_r <= 1'b1;
    endcase

assign	key_out = is_low ? keyout_r : ~keyout_r; 

endmodule